package com.chockqiu.view;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.DecelerateInterpolator;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author Administrator
 */
public class ElementFloatTogetherAnimation extends View {

    private final Paint mPaint = new Paint();
    /**
     * 起始点
     */
    private Point mP0;
    /**
     * 结束点
     */
    private Point mP1;
    /**
     * 控制点
     */
    private final List<Point> mPcs = new ArrayList<>();
    private float[] valueTs;
    private final List<ValueAnimator> mValueAnimators = new ArrayList<>();
    private int elementNum = 20;
    private Drawable elementDrawable;
    private int elementDrawableSize = 24;
    private final Random mRandom = new SecureRandom();
    private boolean mInitFlag = false;
    private long duration = 1500L;
    private int randomDelay = 1200;
    final private AtomicInteger mAtomicInteger = new AtomicInteger();


    public ElementFloatTogetherAnimation(Context context) {
        super(context);
    }

    public ElementFloatTogetherAnimation(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ElementFloatTogetherAnimation(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public ElementFloatTogetherAnimation(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public void init() {
        mInitFlag = true;
        mPaint.setAntiAlias(true);
        mPaint.setColor(Color.BLUE);
        mPaint.setStrokeWidth(3);
        mPaint.setStyle(Paint.Style.STROKE);
        valueTs = new float[elementNum];
        mPcs.clear();
        if (!mValueAnimators.isEmpty()) {
            for (int i = 0; i < elementNum; i++) {
                mValueAnimators.get(i).cancel();
            }
        }
        mValueAnimators.clear();
        mAtomicInteger.set(0);
        double k = centerk(mP0, mP1);
        double d = dp2Px(120) + Math.sqrt((Math.pow(Math.abs(mP0.x - mP1.x), 2)) + (Math.pow(Math.abs(mP0.y - mP1.y), 2))) * 0.4;
        for (int i = 0; i < elementNum; i++) {
            float ddx = 0.2f;
            float ddy = 0.2f;
            float ddyy, ddxx;
            //不同起始点坐标关系, 靠近起点的方向是不一样的
            if (mP0.x == mP1.x) {
                ddxx = 0;
                if (mP0.y > mP1.y) {
                    ddyy = -ddy;
                } else {
                    ddyy = ddy;
                }
            } else if (mP0.y == mP1.y) {
                ddyy = 0;
                if (mP0.x > mP1.x) {
                    ddxx = -ddx;
                } else {
                    ddxx = ddx;
                }
            } else if (mP0.y > mP1.y && mP0.x < mP1.x) {
                //-+
                ddxx = ddx;
                ddyy = -ddy;
            } else if (mP0.y < mP1.y && mP0.x < mP1.x) {
                //--
                ddxx = ddx;
                ddyy = ddy;
            } else if (mP0.y < mP1.y && mP0.x > mP1.x) {
                //+-
                ddxx = -ddx;
                ddyy = ddy;
            } else {
                //++
                ddxx = -ddx;
                ddyy = -ddy;
            }

            Point mPoint = new Point();
            double rd = (mRandom.nextInt(100) - 50) / 50f * 0.6 * d;
            double rdy = 0;
            mPoint.x = (int) ((mP1.x + mP0.x) / (2.0f + ddxx) + dx(k, rd));
            //不同象限, 距离的方向是不一样的
            if (mP0.y > mP1.y && mP0.x < mP1.x) {
                rdy = rd;
            } else if (mP0.y < mP1.y && mP0.x < mP1.x) {
                rdy = -rd;
            } else if (mP0.y < mP1.y && mP0.x > mP1.x) {
                rdy = rd;
            } else {
                rdy = -rd;
            }
            mPoint.y = (int) ((mP1.y + mP0.y) / (2.0f + ddyy) + dy(k, rdy));
            mPcs.add(mPoint);
            ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0, 1);
            mValueAnimator.addUpdateListener(new ValueUpdateListener(valueTs, i));
            mValueAnimator.setInterpolator(new DecelerateInterpolator(1.2f));
            mValueAnimator.setDuration(duration + mRandom.nextInt(randomDelay / 3));
            mValueAnimator.setRepeatCount(0);
            mValueAnimator.setStartDelay(mRandom.nextInt(randomDelay));
            mValueAnimator.addListener(mAnimatorListener);
            mValueAnimators.add(mValueAnimator);
        }
    }

    final private boolean showLines = false;
    final private Path mPath = new Path();

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (!mInitFlag) {
            return;
        }
        //canvas.save();
        //canvas.rotate(-90, getWidth() / 2f, getHeight() / 2f);
        for (int i = 0; i < elementNum; i++) {
            if (showLines) {
                mPath.reset();
                mPath.moveTo(mP0.x, mP0.y);
                mPath.quadTo(mPcs.get(i).x, mPcs.get(i).y, mP1.x, mP1.y);
                canvas.drawPath(mPath, mPaint);
                canvas.drawPoint(mPcs.get(i).x, mPcs.get(i).y, mPaint);
            }
            if (valueTs[i] == 1 || valueTs[i] == 0) {
                //避免多个在同一位置重复绘制
                continue;
            }
            Point vp = quadToFunValue(valueTs[i], mP0, mPcs.get(i), mP1);
            int size = dp2Px(elementDrawableSize) / 2;
            if (elementDrawable != null) {
                elementDrawable.setBounds(vp.x - size, vp.y - size, vp.x + size, vp.y + size);
                elementDrawable.draw(canvas);
            } else {
                canvas.drawPoint(vp.x, vp.y, mPaint);
            }
        }
        //canvas.restore();
    }

    public Drawable getElementDrawable() {
        return elementDrawable;
    }

    public void setElementDrawable(Drawable elementDrawable) {
        this.elementDrawable = elementDrawable;
    }

    public int getElementDrawableSize() {
        return elementDrawableSize;
    }

    public void setElementDrawableSize(int elementDrawableSize) {
        if (elementDrawableSize < 1) {
            return;
        }
        this.elementDrawableSize = elementDrawableSize;
    }

    private boolean callFlag = false;

    public synchronized void startAnimation(Point start, Point end, AnimationEndListener listener) {
        if (start == null || end == null) {
            throw new RuntimeException("起点/终点坐标点不允许为空!");
        }
        if (!callFlag) {
            callFlag = true;
            mAnimationEndListener = () -> {
                callFlag = false;
                if (listener != null) {
                    listener.onAnimationEnd();
                }
            };
            mP0 = start;
            mP1 = end;
            init();
            for (int i = 0; i < elementNum; i++) {
                ValueAnimator mVa = mValueAnimators.get(i);
                mVa.start();
            }
        }
    }

    /**
     * 二次贝塞尔曲线
     *
     * @param t  [0-1] 时间
     * @param p0 起始固定点
     * @param p1 控制点
     * @param p2 结束固定点
     * @return 当前值坐标
     */
    public Point quadToFunValue(float t, Point p0, Point p1, Point p2) {
        if (t > 0 && t < 1) {
            double x, y;
            x = (1 - t) * (1 - t) * p0.x + 2 * (1 - t) * t * p1.x + t * t * p2.x;
            y = (1 - t) * (1 - t) * p0.y + 2 * (1 - t) * t * p1.y + t * t * p2.y;
            return new Point((int) x, (int) y);
        } else if (t == 0) {
            return p0;
        }
        return p2;
    }

    private int dp2Px(float value) {
        Resources resources = getResources();
        DisplayMetrics displayMetrics = resources.getDisplayMetrics();
        return (int) (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, displayMetrics) + 0.5f);
    }

    /*
    中垂线的斜率k
     */
    private double centerk(Point a, Point b) {
        //已知A(Ax,Ay),B(Bx,By); 中垂线的斜率为：
        //-1/k=-1/[(By-Ay)/(Bx-Ax)]=-(Bx-Ax)/(By-Ay)
        if (b.y == a.y) {
            return 0;
        }
        if (b.x == a.x) {
            return Double.MAX_VALUE;
        }
        return -(((b.x - a.x) * 1f / (b.y - a.y)));
    }

    /**
     * 斜率k时距离d对应的dx值
     *
     * @param k 斜率k
     * @param d 距离d
     * @return
     */
    private double dx(double k, double d) {
        if (k == 0) {
            return 0;
        }
        if (k == Double.MAX_VALUE) {
            return d;
        }
        double dx = Math.sqrt((d * d) / (k * k + 1));
        if (d < 0) {
            return -dx;
        } else {
            return dx;
        }
    }

    /**
     * 斜率k时距离d对应的dy值
     *
     * @param k 斜率k
     * @param d 距离d
     * @return
     */
    private double dy(double k, double d) {
        if (k == 0) {
            return d;
        }
        if (k == Double.MAX_VALUE) {
            return 0;
        }
        double dy = Math.sqrt((d * d) * (k * k) / (k * k + 1));
        if (d < 0) {
            return -dy;
        } else {
            return dy;
        }
    }

    public long getDuration() {
        return duration;
    }

    public void setDuration(long duration) {
        if (duration < 0) {
            return;
        }
        this.duration = duration;
    }

    public int getRandomDelay() {
        return randomDelay;
    }

    public void setRandomDelay(int randomDelay) {
        if (duration < 0) {
            return;
        }
        this.randomDelay = randomDelay;
    }

    public int getElementNum() {
        return elementNum;
    }

    public void setElementNum(int elementNum) {
        this.elementNum = elementNum;
    }

    private Animator.AnimatorListener mAnimatorListener = new Animator.AnimatorListener() {

        @Override
        public void onAnimationStart(Animator animation) {

        }

        @Override
        public void onAnimationEnd(Animator animation) {
            int num = mAtomicInteger.incrementAndGet();
            if (num >= elementNum) {
                if (mAnimationEndListener != null) {
                    mAnimationEndListener.onAnimationEnd();
                }
            }
        }

        @Override
        public void onAnimationCancel(Animator animation) {

        }

        @Override
        public void onAnimationRepeat(Animator animation) {

        }
    };

    public AnimationEndListener mAnimationEndListener;

    public interface AnimationEndListener {
        /**
         * 动画结束时回调
         */
        void onAnimationEnd();
    }

    private class ValueUpdateListener implements ValueAnimator.AnimatorUpdateListener {

        private float[] valueTs;
        private int index = 0;

        ValueUpdateListener(float[] valueTs, int index) {
            this.valueTs = valueTs;
            this.index = index;
        }

        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            valueTs[index] = (float) animation.getAnimatedValue();
            invalidate();
        }
    }

}
